package com.zzzzzz.taglib.ui;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

import com.zzzzzz.common.util.Validator;

public class PageIteratorTag extends TagSupport {

	@Override
	public int doEndTag() throws JspException {
		try {
			JspWriter jspWriter = pageContext.getOut();

			String results = getResults(cur - 1);
			String links = getLinks(cur - 1);

			StringBuffer sb = new StringBuffer();
			sb.append("<div class=\"taglib-page-iterator\">");
			sb.append("<div class=\"search-results\">");
			sb.append(results);
			sb.append("</div>");
			sb.append("<div class=\"search-pages\">");
			sb.append(links);
			sb.append("</div></div>");
			
			jspWriter.write(sb.toString());
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return EVAL_PAGE;
	}
	
	@Override
	public int doStartTag() throws JspException {
		return SKIP_BODY;
	}
	
	private String getResults(int cur) {
		if(cur > getNumPages() || cur < 0) {
			return "Showing 0 result";
		}
		int start = ((cur) * itemsPerPage) + 1;
		int end = start - 1 + itemsPerPage;
		if(end > total) {
			end = total;
		}
		if(total < itemsPerPage) {
			if(total <= 1) {
				return "Showing " + total + " result.";
			}else {
				return "Showing " + total + " results.";
			}
		}else {
			return "Showing " + start + " - " + end + " of " + total + " results.";
		}
	}
	
	private String getLinks(int cur) {
		StringBuffer links_sb = new StringBuffer();
		int begin, end;
		int np = getNumPages();
		Map<String, Integer> interval = getInterval(cur);
		
		// Generate "Previous" - Link
		if(cur > 0 || prev_show_always) {
			Map<String, String> appendOpts = new HashMap<String, String>();
			appendOpts.put("text", "Prev");
			appendOpts.put("classes", "prev");
			links_sb.append(createLink(cur, cur - 1, appendOpts));
		}
		
		// Generate starting points
		if(interval.get("start") > 0 && numEdgePages > 0) {
			end = Math.min(numEdgePages, interval.get("start"));
			links_sb.append(appendRange(cur, 0, end));
			if(numEdgePages < interval.get("start")) {
				links_sb.append("<span>...</span>");
			}
		}
		
		// Generate interval links
		links_sb.append(appendRange(cur, interval.get("start"), interval.get("end")));
		
		// Generate ending points
		if(interval.get("end") < np && numEdgePages > 0) {
			if((np - numEdgePages) > interval.get("end")) {
				links_sb.append("<span>...</span>");
			}
			begin = Math.max(np - numEdgePages, interval.get("end"));
			links_sb.append(appendRange(cur, begin, np));
		}
		
		// Generate "Next" - Link
		if(cur < (np - 1) || next_show_always) {
			Map<String, String> appendOpts = new HashMap<String, String>();
			appendOpts.put("text", "Next");
			appendOpts.put("classes", "next");
			links_sb.append(createLink(cur, cur + 1, appendOpts));
		}
		
		return links_sb.toString();
	}
	
	private String appendRange(int cur, int start, int end) {
		StringBuffer links_sb = new StringBuffer();
		for(int i=start; i<end; i++) {
			links_sb.append(createLink(cur, i, new HashMap<String, String>()));
		}
		return links_sb.toString();
	}
	
	private String createLink(int cur, int page_id, Map<String, String> appendOpts) {
		String link = "";
		int np = getNumPages();
		page_id = page_id < 0 ? 0 : (page_id < np ? page_id : np-1);  // Normalize page id to sane value
		String text = appendOpts.get("text");
		String classes = appendOpts.get("classes");
		if(Validator.isNull(text)) {
			text = String.valueOf(page_id + 1);
		}
		
		if(page_id == cur) {
			if(Validator.isNull(classes)) {
				link = "<span class=\"current\">" + text + "</span>";
			}else {
				link = "<span class=\"current " + classes + "\">" + text + "</span>";
			}
		}else {
			if(Validator.isNull(classes)) {
				link = "<a href=\"" + actionPrefixURL + (page_id + 1) + "\">" + text + "</a>";
			}else {
				link = "<a href=\"" + actionPrefixURL + (page_id + 1) + "\" class=\"" + classes + "\">" + text + "</a>";
			}
		}
		return link;
	}
	
	private Map<String, Integer> getInterval(int cur) {
		Map<String, Integer> interval = new HashMap<String, Integer>();
		int ne_half = (int) Math.floor(numDisplayPages/2);
		int np = getNumPages();
		int upper_limit = np - numDisplayPages;
		
		int start = cur > ne_half ? Math.max(Math.min(cur - ne_half, upper_limit), 0) : 0;
		int end = cur > ne_half ? Math.min(cur + ne_half + (numDisplayPages % 2), np) : Math.min(numDisplayPages, np);
		
		interval.put("start", start);
		interval.put("end", end);
		return interval;
	}
	
	private int getNumPages() {
		if(total == 0) {
			return 1;
		}
		return (int) Math.ceil((double) total/itemsPerPage);
	}

	public void setTotal(int total) {
		this.total = total;
	}

	public void setCur(int cur) {
		this.cur = cur;
	}

	public void setActionPrefixURL(String actionPrefixURL) {
		this.actionPrefixURL = actionPrefixURL;
	}

	public void setItemsPerPage(int itemsPerPage) {
		this.itemsPerPage = itemsPerPage;
	}

	public void setNumDisplayPages(int numDisplayPages) {
		this.numDisplayPages = numDisplayPages;
	}

	public void setNumEdgePages(int numEdgePages) {
		this.numEdgePages = numEdgePages;
	}

	private int total;
	
	private int cur;
	
	private String actionPrefixURL;
	
	private int itemsPerPage;
	
	private int numDisplayPages;
	
	private int numEdgePages;
	
	private static boolean prev_show_always = true;

	private static boolean next_show_always = true;
	
}
